package com.gitee.apanlh.util.thread.pool;

import com.gitee.apanlh.util.log.Log;
import com.gitee.apanlh.util.thread.Sleep;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;
import java.util.concurrent.TimeUnit;

/**
 * 	线程池工具类
 *
 * 	@author Pan
 */
public class ThreadPoolExecutorUtils {
	
	/**
	 * 	构造函数
	 * 
	 * 	@author Pan
	 */
	private ThreadPoolExecutorUtils() {
		//	不允许外部实例
		super();
	}

	/**
	 *  callerRunsPolicyHandler（调用者策略）
	 *	<br>当有新任务提交后，如果线程池没被关闭且没有能力执行，则把这个任务交于提交任务的线程执行，也就是谁提交任务，谁就负责执行任务
	 *
	 * 	@return DiscardPolicy
	 */
	public static CallerRunsPolicy callerRunsPolicyHandler() {
		return new ThreadPoolExecutor.CallerRunsPolicy() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
				super.rejectedExecution(r, e);
				Log.get().info("callerRunsPolicy error");
			}
		};
	}

	/**
	 *  默认的拒绝策略（取消策略）
	 *	<br>丢弃该任务并抛出RejectedExecutionException异常。抛出异常让开发者发现
	 *
	 * 	@return DiscardPolicy
	 */
	public static AbortPolicy abortPolicyHandler() {
		return new ThreadPoolExecutor.AbortPolicy() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
				super.rejectedExecution(r, e);
				Log.get().info("abortPolicy error");
			}
		};
	}

	/**
	 *  DiscardOldestPolicy（丢弃最旧策略）
	 *	<br>丢弃队列最前面的任务，然后重新提交被拒绝的任务。因为最前面的任务肯定是执行时间最久的<br>
	 *
	 * 	@return DiscardPolicy
	 */
	public static DiscardOldestPolicy discardOldestPolicyHandler() {
		return new ThreadPoolExecutor.DiscardOldestPolicy() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
				super.rejectedExecution(r, e);
				Log.get().info("discardOldestPolicy error");
			}
		};
	}

	/**
	 *  discardPolicyHandler(丢弃策略)
	 *	<br>丢弃该任务不抛出异常，线程队列已满的情况下，后进来的任务都丢弃
	 *	<br>相比默认的拒绝策略，这个策略就显得不怎么重要，适用于一些不重要的业务，如统计一些无关紧要但是又需要的数据。
	 *
	 * 	@return DiscardPolicy
	 */
	public static DiscardPolicy discardPolicyHandler() {
		return new ThreadPoolExecutor.DiscardPolicy() {
			@Override
			public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
				super.rejectedExecution(r, e);
				Log.get().info("discardPolicy error");
			}
		};
	}

	/**
	 *	初始化阻塞任务队列线程池
	 *
	 * 	@param 	poolSize	线程大小
	 * 	@return	ThreadPoolExecutor
	 */
	public static ThreadPoolExecutor instanceBlocking(int poolSize) {
		return instanceThreadPool(
			poolSize, 
			poolSize,
			10L,
			TimeUnit.SECONDS,
			new LinkedBlockingQueue<Runnable>(), 
			callerRunsPolicyHandler(), 
			true
		);
	}
	
	/**	
	 * 	初始化阻塞任务队列线程池
	 *
	 * 	@author Pan
	 * 	@param 	corePoolSize		核心线程数
	 * 	@param 	maximumPoolSize		最大线程数
	 * 	@return ThreadPoolExecutor
	 */
	public static ThreadPoolExecutor instanceBlocking(int corePoolSize, int maximumPoolSize) {
		return instanceThreadPool(
			corePoolSize, 
			maximumPoolSize,
			10L,
			TimeUnit.SECONDS,
			new LinkedBlockingQueue<Runnable>(), 
			callerRunsPolicyHandler(), 
			true
		);
	}
	
	/**	
	 * 	初始化阻塞任务队列线程池
	 *
	 * 	@author Pan
	 * 	@param 	corePoolSize		核心线程数
	 * 	@param 	maximumPoolSize		最大线程数
	 * 	@param 	handler				拒绝策略
	 * 	@return ThreadPoolExecutor
	 */
	public static ThreadPoolExecutor instanceBlocking(int corePoolSize, int maximumPoolSize, RejectedExecutionHandler handler) {
		return instanceThreadPool(
			corePoolSize, 
			maximumPoolSize,
			10L,
			TimeUnit.SECONDS,
			new LinkedBlockingQueue<Runnable>(), 
			handler, 
			true
		);
	}

	/**
	 *	初始化阻塞任务队列线程池
	 *
	 * 	@author Pan
	 * 	@param 	corePoolSize	核心线程数
	 * 	@param 	maximumPoolSize	最大线程数
	 * 	@param 	keepAliveTime	空闲线程存活时间
	 * 	@param 	unit			时间单位
	 * 	@param 	workQueue		线程池任务队列
	 * 	@param 	handler			拒绝策略
	 * 	@param 	allowCoreThreadTimeOut	是否允许核心线程空闲超时时被回收
	 * 	@return ThreadPoolExecutor
	 */
	private static ThreadPoolExecutor instanceThreadPool(int corePoolSize, int maximumPoolSize,
														 long keepAliveTime, TimeUnit unit,
														 BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler,
														 boolean allowCoreThreadTimeOut) {

		ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
			corePoolSize,
			maximumPoolSize,
			keepAliveTime,
			unit,
			workQueue,
			handler
		);

		if (allowCoreThreadTimeOut) {
			threadPoolExecutor.allowCoreThreadTimeOut(true);
		}
		return threadPoolExecutor;
	}
	
	/**
	 * 	执行线程池
	 * 	<br>执行完自动关闭
	 * 	<br>阻塞任务队列
	 * 	<br>最大10个线程执行任务
	 * 	
	 * 	@author Pan
	 * 	@param 	taskCount	执行次数
	 * 	@param 	runnable	线程
	 */
	public static void blockingExecute(int taskCount, Runnable runnable) {
		ThreadPoolExecutor threadPoolExecutor = instanceBlocking(10, 10);
		for (int i = 1; i <= taskCount; i++) {
			threadPoolExecutor.execute(runnable);
		}
		stopThread(threadPoolExecutor);
	}

	
	/**	
	 * 	异步执行
	 * 	<br>创建线程池
	 * 	<br>阻塞队列
	 * 	<br>最大10个队列完成
	 * 	<br>任务执行后关闭线程池
	 * 	
	 * 	@author Pan
	 * 	@param 	taskCount	执行次数
	 * 	@param 	runnable	线程
	 */
	public static void execute(int taskCount, Runnable runnable) {
		execute(10, taskCount, runnable);
	}
	
	/**	
	 * 	异步执行
	 * 	<br>阻塞队列
	 * 	<br>指定线程核心数
	 * 
	 * 	@author Pan
	 * 	@param 	threadCoreSize	核心线程大小
	 * 	@param 	taskCount	执行次数
	 * 	@param 	runnable	线程
	 */
	public static void execute(int threadCoreSize, int taskCount, Runnable runnable) {
		ThreadPoolExecutor threadPoolExecutor = instanceBlocking(threadCoreSize);
		execute(threadPoolExecutor, taskCount, runnable);
		threadPoolExecutor.shutdown();
	}
	
	/**	
	 * 	异步执行
	 * 
	 * 	@author Pan
	 * 	@param 	threadPoolExecutor	线程池
	 * 	@param 	taskCount			执行次数
	 * 	@param 	runnable			线程
	 */
	public static void execute(ThreadPoolExecutor threadPoolExecutor, int taskCount, Runnable runnable) {
		for (int i = 1; i <= taskCount; i++) {
			threadPoolExecutor.execute(runnable);
		}
	}
	
	/**	
	 * 	停止线程池
	 * 	<br>阻塞主线程
	 * 	
	 * 	@author Pan
	 * 	@param 	threadPoolExecutor	线程池
	 */
	public static void stopThread(ThreadPoolExecutor threadPoolExecutor) {
		threadPoolExecutor.shutdown();
		while (!threadPoolExecutor.isTerminated()) {
			Sleep.mills(50);
		}
	}
}
